将小米平板2从低版本安卓适配到高版本的若干收获与思考

您所在的位置:网站首页 cm211-2 安卓版本 将小米平板2从低版本安卓适配到高版本的若干收获与思考

将小米平板2从低版本安卓适配到高版本的若干收获与思考

2023-03-28 01:45| 来源: 网络整理| 查看: 265

有个愿望:让小米平板2用上64位 Android9.0。因为64位 Android9.0 的 houdini 兼容性非常好,我曾经试过把 houdini9 植入 Android_x86-9.0 r2,然后安装到小米平板2中,发现APP运行很稳定,其它小米平板2安卓系统常见的APP闪退问题基本上都没有。只是因为显卡原因,Android_x86-9.0 r2 在小米平板2上只能运行在 nomodeset 模式下,视频靠CPU解码,导致系统卡顿很厉害。

目标很美好,但对于安卓适配新兵来说,实现这个目标还是要循序渐进。

我手头有能成功编译的 lineage13.0 for latte(小米平板2的代号是 latte)源码,lineage13.0 属于安卓6,为了缩小跨度,降低难度,我决定先从 lineage14.1 的小米平板2适配入手,因为 lineage14.1 属于安卓7,相比安卓6的变化比9要小一些。

虽然适配工作还没有完成,但过程中也有不少收获,总结一下。

一、下载源码

lineage14.1 的主源码可以按 LineageOS Wiki 上的说明下载,latte 的 vendor、device、kernel 则来自 Github( LineageOS Resources for Xiaomi Mi Pad 2 ),但配置 local_mainifest.xml 时要注意:不要使用 cm-14.1 分支的 vendor、device、kernel,它的BUG太多,基于它进行适配工作量巨大,并且会出现很多看不懂的问题。建议用 cm-13.0 分支,因为它已经在 lineage13.0 for latte上适配成功,本身不存在问题,所需要做的是对 lineage14.1 调试。

二、获取日志

最初编译出来的 lineage14.1 Rom 安装到小米平板2上多半无法成功运行,需要查看日志来分析原因,但系统如果没有运行到合适的阶段是无法通过 ADB 获取日志的,怎么办?群友 ygjsz 提供了一个方法。

在 init.rc 中加入以下代码:

# Take logs even when adb is not working. # Thanks: markakash # Add these lines to your init.rc file: service boot_lc_main /system/bin/logcat -f /cache/boot_lc_main.txt class main user root group root system oneshot service boot_dmesg /system/bin/sh -c "dmesg -w > /cache/boot_dmesg.txt" class main user root group root system oneshot on property:sys.boot_completed=1 start boot_lc_main start boot_dmesg

这段代码在开机后会自动把日志记录到 cache 文件夹中。当系统启动失败后,长按电源键+音量增键,让平板进入 Recovery 模式,就能把日志拷贝出来分析。

三、处理 dlopen failed 错误

日志中常出现 dlopen failed 报错,形式为 cannot locate symbol "_ZNxxxx" referenced by "xxxxx.so",例如:

dlopen failed: cannot locate symbol "_ZN7android13GraphicBufferC1Ejjij" referenced by "/system/vendor/lib/hw/hwcomposer.gmin.so"

这里需要重点搞清 symbol "_ZN7androidxxxxxx" 是什么?简单说,它是系统在编译 so 时自动生成的某个函数的别名,这个函数在源码中本来有易理解的名称,但在编译时编译器给它生成了一个 symbol,这个 symbol 具有唯一性,生成规则与函数名称、参数个数和参数类型有关,系统运行时根据它就可以定位到具体的动态库中具体的函数。

解决这个报错的第一步是要定位这个函数,但由于不知道生成规则,所以无法直接解码 symbol 去定位函数源码,但我找到了一个方法。

报错中的 xxxxx.so 一般都位于 system/vendor/lib 中,因为用的是 lineage13.0 for latte 的 vendor, lineage14.1 中找不到的那个 symbol 在 lineage13.0 一定能找到,所以可以先到 lineage13.0 中找,确定函数位于哪个源码文件中,然后再看 lineage14.1 中这个文件为什么没有这个函数。

就以 dlopen failed: cannot locate symbol "_ZN7android13GraphicBufferC1Ejjij" referenced by "/system/vendor/lib/hw/hwcomposer.gmin.so" 举例。

先看 "_ZN7android13GraphicBufferC1Ejjij" 位于 lineage13.0的哪个动态链接库中:

butterfly@Amazon:~/lineage/13.0/out/target/product/latte/system/lib$ find -type f -name '*.so' | xargs grep "_ZN7android13GraphicBufferC1Ejjij"

出现以下结果:

匹配到二进制文件 ./libstagefright_wfd.so 匹配到二进制文件 ./libshim_camera.so 匹配到二进制文件 ./libgui.so 匹配到二进制文件 ./hw/camera.gmin.so 匹配到二进制文件 ./arm/libgui.so 匹配到二进制文件 ./arm/libui.so 匹配到二进制文件 ./arm/nb/libgui.so 匹配到二进制文件 ./arm/nb/libui.so 匹配到二进制文件 ./libui.so 匹配到二进制文件 ./libwebviewchromium_plat_support.so

说明这些so动态链接文件中都有 "_ZN7android13GraphicBufferC1Ejjij",但有并不表示这个函数在这个文件中,有可能这个文件只是调用了这个函数而已,那么哪个才是被调用的文件呢?

对这些文件逐个执行下面命令:

butterfly@Amazon:~/lineage/13.0$ ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc-nm -D ~/lineage/13.0/out/target/product/latte/system/lib/libui.so | grep GraphicBuffer | sort

命令中,“GraphicBuffer”是 symbol 中一段有字面含义的文字,它一般是那个函数名称中的一部分。

出现下面结果:

00007930 T _ZN7android13GraphicBufferC1Ev 00007930 T _ZN7android13GraphicBufferC2Ev 00007a20 T _ZN7android13GraphicBufferC1Ejjij 00007a20 T _ZN7android13GraphicBufferC2Ejjij 00007b30 T _ZN7android13GraphicBuffer8initSizeEjjij 00007c00 T _ZN7android13GraphicBufferC1EjjijjP13native_handleb ......

可以看到第三行就是那个 symbol,它前面有一串十六进制数 00007a20,这表明这个 symbol 在这个so文件中对应着有实实在在的函数;如果这个 symbol 前面没有十六进制字符串,则表明这个 symbol 出现在这个文件中只是因为这个文件调用了别的库中的这个函数。通过这个方法可以找到有这个函数的so文件,在这个例子中就是 system/lib/libui.so(其实有一个方法可以减轻工作量,不用对每个so文件都检查:用 Cutter 工具打开 hwcomposer.gmin.so,查看它的依赖库,仅对是其依赖库的so文件进行分析)。

上面的命令中,x86_64-linux-android-gcc-nm 的路径是计算机中它的安装位置,我的安装在 ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/ 中。

下面查这个so文件的确切函数名称和参数。对 system/lib/libui.so 执行下面这条命令(与上一条很像,但多一个 “-C”):

butterfly@Amazon:~/lineage/13.0$ ~/Android/Sdk/ndk/16.1.4479499/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc-nm -C -D ~/lineage/13.0/out/target/product/latte/system/lib/libui.so | grep GraphicBuffer | sort

出现下面结果:

00007930 T android::GraphicBuffer::GraphicBuffer() 00007930 T android::GraphicBuffer::GraphicBuffer() 00007a20 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int) 00007a20 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int) 00007b30 T android::GraphicBuffer::initSize(unsigned int, unsigned int, int, unsigned int) 00007c00 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool) 00007c00 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool) 00007d00 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool) 00007d00 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool) 00007e10 T android::GraphicBuffer::~GraphicBuffer() 00007e10 T android::GraphicBuffer::~GraphicBuffer() ......

找到 00007a20 那行,其对应的是 GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int),这就是那个函数的名称与参数。

然后查找函数源码。执行下面命令:

butterfly@Amazon:~/lineage/13.0$ find -type f -name '*.mk' | xargs grep "LOCAL_MODULE := libui"

出现:

./frameworks/native/libs/ui/Android.mk:LOCAL_MODULE := libui

由此可知 libui 的源码在 frameworks/native/libs/ui/ ,进到这个文件夹,运行:

butterfly@Amazon:~/lineage/13.0/frameworks/native/libs/ui$ find -type f -name '*.cpp' | xargs grep "GraphicBuffer::GraphicBuffer("

出现:

./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer() ./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, ./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, ./GraphicBuffer.cpp:GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)

至此,可以确定那个 symbol 对应的函数在 frameworks/native/libs/ui/GraphicBuffer.cpp 中。

打开 ~/lineage/13.0/frameworks/native/libs/ui/GraphicBuffer.cpp,可以找到 GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int)。打开 Lineage14.1 中同名 文件,发现尽管其中也有 GraphicBuffer::GraphicBuffer 函数,但所有的 GraphicBuffer 函数中没有参数为 (unsigned int, unsigned int, int, unsigned int) 的。

现在可以动手解决 dlopen failed 错误了:参照 lineage13.0 中函数内容,在 lineage14.1 中添加相同参数的 GraphicBuffer 函数。这个函数怎么写要具体情况具体分析,即要实现其应有的功能,又不能破坏原有的函数。

添加完,编译后, 让平板进入REC 模式,将 lineage14.1 中新生成的 out/target/product/latte/system/lib/libui.so 拷贝到平板中,再开机获取日志,可以看到那条 dlopen failed 报错不再出现了。

其它 dlopen failed 错误也可以参考这个方法解决。

但有一个类似报错需要用到不同的处理方法,报错形式是:Can't locate symbol "ufoInitPlatform" referenced by ......

这个问题折磨我很久才发现是因为系统中有两个 libskuwa.so,一个在 system/lib 中,一个在 vendor/lib 中,默认会调用 system/lib 下那个,而那个是没有 ufoInitPlatform 函数的。解决办法就是把 system/lib 下面那个 libskuwa.so 删除。

通过前面这些处理方法,我成功地实现了小米平板2的开机动画显示。

https://www.zhihu.com/video/1473386188496752640四、目前遇到的困难

适配工作还在艰难进行中,不断遇到新的问题。目前主要有三个问题不知道该怎么分析。

1.keymaster

日志中有下面报错重复出现:

3336 3336 I keystore: Found keymaster0 module Keymaster Intel MEI HAL, version 3 3336 3336 I SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 131: Creating device 3336 3336 D SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 132: Device address: 0xf6eb0000 3336 3336 I ITEEKeyMaster: intel_mei_km_open called 3336 3336 I ITEEKeyMaster: Connected to keymasterd successfully 3336 3336 I ITEEKeyMaster: get_supported_classes called 2904 3337 E ITEEKeyMasterD: Could not acquire wake lock ret = -2 3336 3336 E ITEEKeyMaster: Received error -2 on command 0 from FW 3336 3336 E ITEEKeyMaster: CMD_ID_GET_SUPPORTED_CLASSES failed 3336 3336 E keystore: Error opening keystore keymaster0 device. 3336 3336 E keystore: keystore keymaster could not be initialized; exiting

我能跟踪到报错来自 keystore.gmin.so,由于没有它的源码,无法进一步分析。

尝试用 keystore.default.so 替换 keystore.gmin.so,日志中报错消失,变成下面的内容,不确定是否解决。

3045 3045 I keystore: Found keymaster0 module Keymaster OpenSSL HAL, version 2 3045 3045 I SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 131: Creating device 3045 3045 D SoftKeymaster: system/keymaster/soft_keymaster_device.cpp, Line 132: Device address: 0xf6eb0000 3045 3045 I keystore: Keymaster0 module is software-only. Using SoftKeymasterDevice instead.

2.sensorhubd

报错如下,不知道该如何分析。

3335 3335 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 3335 (sensorhubd) 3338 3338 E : debuggerd: Unable to connect to activity manager (connect failed: Connection refused) 3338 3338 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 3338 3338 F DEBUG : LineageOS Version: '14.1-20220120-UNOFFICIAL-latte' 3338 3338 F DEBUG : Build fingerprint: 'Xiaomi/latte/latte:5.1/LMY47I/V8.2.2.0.LACCNDL:user/release-keys' 3338 3338 F DEBUG : Revision: '0' 3338 3338 F DEBUG : ABI: 'x86' 3338 3338 F DEBUG : pid: 3335, tid: 3335, name: sensorhubd >>> /system/bin/sensorhubd


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3